// Author	: damphat@gmail.com
// Created	: 01/12/2008

#pragma once
#include "vn/NetCommon.h"
#include "vn/Stream.h"
#include "vn/Socket.h"


NET_BEGIN

using IO::Stream;
using IO::SeekOrigin;

class NetworkStream : public Stream {
public:
	NetworkStream() 
		: m_canRead(false)
		, m_canWrite(false)
		, m_sock(0)
		, m_own(false)
	{ }

	void Attach(Socket *sock, bool own = false) {
		// if(own == true) 
		//    check for sure: sock is not stack object
		//	  notify user don't delete the sock
		// else
		//	  user must have have another way to delete sock
		CHECK_ARG_NOT_NULL(sock);
		CHECK_ARG(sock->GetSocketType()	== SOCK_STREAM);
		CHECK_ARG(sock->GetBlocking()	== true);
		CHECK_ARG(sock->GetStatus()		== StatusConnected);

		CHECK_STATUS(m_sock == 0);

		m_sock		= sock;
		m_own		= own;
		m_canRead	= true;
		m_canWrite	= true;
	}

	// S - add
	void Shutdown(SocketShutdown direction) {
		CHECK_STATUS(m_sock != 0);
		m_sock->Shutdown(direction);
		if(direction == ShutdownReceive) {
			m_canRead = false;
		}
		else if(direction == ShutdownSend) {
			m_canWrite = false;
		}
		else {
			m_canRead = false;
			m_canWrite = false;
		}
	}
	// E - add

	virtual StmLen Read(Buf8 *buf, StmLen len) { 
		CHECK_ARG_BUFFER(buf, len);
		CHECK_ARG_RANGE(len, 0, INT_MAX);
		CHECK_STATUS(m_sock);
		return m_sock->Recv(buf, (int)len);		
	}

	virtual void  Write(const Buf8 *buf, StmLen len) {
		CHECK_ARG_BUFFER(buf, len);
		CHECK_ARG_RANGE(len, 0, INT_MAX);
		CHECK_STATUS(m_sock);
		int totalSend = 0;
		while(totalSend < len) {
			int nsend = m_sock->Send(buf + totalSend, (int)len - totalSend);
			assert(nsend == len); // always true?
			totalSend += nsend;
		}
	}

	virtual StmPos Seek(StmOff offset, SeekOrigin origin) { 
		STREAM_NOT_SUPPORTED;
		return 0; 
	}

	virtual void  Flush() {
		CHECK_STATUS(m_sock);
	}

	virtual void  Close() {
		if(m_sock) {
			m_canRead	= false;
			m_canWrite	= false;
			if (m_own) {
				m_sock->Close();
				delete m_sock;
			}
			m_sock		= 0;
			m_own		= false;
		}
	}

	virtual bool  CanRead() { return m_canRead; }
	virtual bool  CanWrite() { return m_canWrite; }
	virtual bool  CanSeek()  { return false; }

	virtual StmPos GetPosition() {
		STREAM_NOT_SUPPORTED
		return 0; 
	}

	virtual void  SetPosition(StmPos pos) {
		STREAM_NOT_SUPPORTED
	}

	virtual StmLen GetLength() { 
		STREAM_NOT_SUPPORTED
		return 0;
	}

	virtual void  SetLength(StmLen len) {
		STREAM_NOT_SUPPORTED
	}

	virtual ~NetworkStream() {
		Close();
	}

private:
	bool	m_canRead;
	bool	m_canWrite;

	Socket*	m_sock;
	bool	m_own;
};

NET_END
